DynamoDBの query() でソートキーに条件を指定したとき、読み込みキャパシティーユニットが減るのか試してみた
DynamoDBテーブルで複数のデータを取得するとき、効率よくデータを取得することを意識します。 これらは、パーティションキーやソートキーの設計、GSIの設計などに関係します。
そこで、ふと気になりました。
「query()
のソートキーに条件を指定したら、読み込みキャパシティーユニットは減るのだろうか?」と。
試してみました。
おすすめの方
- boto3でDynamoDBテーブルの
query()
を利用したい方 query()
のソートキーに条件を指定したととき、読み込みキャパシティーユニットが減るのか知りたい方
実験用のDynamoDBテーブルを作成して、データを格納する
DynamoDBテーブルを作成する
実験用のデータを格納する
次のスクリプトを実行してDynamoDBテーブルにデータを格納します。
import boto3 def main(): dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('device-data-table') with table.batch_writer() as batch: for i in range(1000): batch.put_item(Item={ 'deviceId': 'device1', 'timestamp': i, 'message': f'hello {i}', }) with table.batch_writer() as batch: for i in range(1000): batch.put_item(Item={ 'deviceId': 'device2', 'timestamp': i * 2, 'message': f'hello {i * 2}', }) if __name__=='__main__': main()
python put_item.py
query() を実行して、読み込みキャパシティーユニットを把握する
スクリプト
2つのquery()
を実行して比較します。
- パーティションキーを指定する
- パーティションキーを指定し、ソートキーで範囲を指定する
それぞれの結果について、次の3つの情報を取得します。
- Count
- 実際のデータ数
- ScannedCount
- フィルター適応前のデータ数
- ConsumedCapacity
- 消費したキャパシティーユニット
import boto3 from boto3.dynamodb.conditions import Key def main(): dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('device-data-table') query_normal(table, 'device1') query_sk(table, 'device1', 200) def query_normal(table, device_id): options = { 'KeyConditionExpression': Key('deviceId').eq(device_id), 'ReturnConsumedCapacity': 'TOTAL', } while True: resp = table.query(**options) print(f'Count: {resp["Count"]}, ScannedCount: {resp["ScannedCount"]}, ConsumedCapacity: {resp["ConsumedCapacity"]}') if 'LastEvaluatedKey' not in resp: break options['ExclusiveStartKey'] = resp['LastEvaluatedKey'] def query_sk(table, device_id, timestamp): options = { 'KeyConditionExpression': Key('deviceId').eq(device_id) & Key('timestamp').lt(timestamp), 'ReturnConsumedCapacity': 'TOTAL', } while True: resp = table.query(**options) print(f'Count: {resp["Count"]}, ScannedCount: {resp["ScannedCount"]}, ConsumedCapacity: {resp["ConsumedCapacity"]}') if 'LastEvaluatedKey' not in resp: break options['ExclusiveStartKey'] = resp['LastEvaluatedKey'] if __name__=='__main__': main()
python query.py
実行結果
取得したデータ数、および、読み込みキャパシティーユニット量のそれぞれに違いがあります。 つまり、ソートキーに条件を指定すると、読み込みキャパシティーユニット量が減りました。
Count: 1000, ScannedCount: 1000, ConsumedCapacity: {'TableName': 'device-data-table', 'CapacityUnits': 5.5} Count: 200, ScannedCount: 200, ConsumedCapacity: {'TableName': 'device-data-table', 'CapacityUnits': 1.5}
おまけ
せっかくなので、query()
でフィルターを指定してみました。
import boto3 from boto3.dynamodb.conditions import Attr, Key def main(): dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('device-data-table') query_filter(table, 'device1', 200) def query_filter(table, device_id, timestamp): options = { 'KeyConditionExpression': Key('deviceId').eq(device_id) & Key('timestamp').lt(timestamp), 'FilterExpression': Attr('message').contains('11'), 'ReturnConsumedCapacity': 'TOTAL', } while True: resp = table.query(**options) print(f'Count: {resp["Count"]}, ScannedCount: {resp["ScannedCount"]}, ConsumedCapacity: {resp["ConsumedCapacity"]}') if 'LastEvaluatedKey' not in resp: break options['ExclusiveStartKey'] = resp['LastEvaluatedKey'] if __name__=='__main__': main()
python omake.py
フィルターは、取得したデータに対して結果を返す前に適用されることが分かります。
Count: 11, ScannedCount: 200, ConsumedCapacity: {'TableName': 'device-data-table', 'CapacityUnits': 1.5}
さいごに
あらためて、DynamoDBテーブルのパーティションキーとソートキー、およびGSIの設計が大事だと実感しました。